package com.tl.satoken.config;

import com.tl.satoken.netty.WebSocketFrameHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import javax.net.ssl.KeyManagerFactory;
import java.io.InputStream;
import java.security.KeyStore;

/**
 * Netty WebSocket 服务器配置类
 * 负责启动和管理基于Netty的WebSocket服务器
 * 
 * 主要功能：
 * 1. 配置Netty服务器启动参数
 * 2. 设置Channel处理器管道
 * 3. 管理服务器生命周期
 */
@Slf4j
@Component
public class NettyWebSocketConfig {

    /** WebSocket服务器端口，默认8081 */
    @Value("${netty.websocket.port:8081}")
    private int port;

    /** WebSocket连接路径，默认/ws */
    @Value("${netty.websocket.path:/ws}")
    private String path;

    /** JKS证书文件路径 */
    @Value("${netty.ssl.keystore:www.lonelycosmo.com.jks}")
    private String keystorePath;

    /** JKS证书密码 */
    @Value("${netty.ssl.keystore-password:}")
    private String keystorePassword;

    /** 是否启用SSL */
    @Value("${netty.ssl.enabled:false}")
    private boolean sslEnabled;

    @Autowired
    private WebSocketFrameHandler webSocketFrameHandler;

    /** 主线程组，用于接收客户端连接 */
    private EventLoopGroup bossGroup;
    
    /** 工作线程组，用于处理客户端请求 */
    private EventLoopGroup workerGroup;
    
    /** 服务器启动结果，用于管理服务端Channel */
    private ChannelFuture channelFuture;

    /** SSL上下文 */
    private SslContext sslContext;

    /**
     * 启动Netty WebSocket服务器
     * 在Spring容器启动后自动调用
     */
    @PostConstruct
    public void start() throws InterruptedException {
        // 如果启用SSL，初始化SSL上下文
        if (sslEnabled) {
            initSslContext();
        }
        
        // 创建主线程组，用于接收客户端连接请求（一般一个线程就够）
        bossGroup = new NioEventLoopGroup(1);
        // 创建工作线程组，用于处理已建立连接的数据读写
        workerGroup = new NioEventLoopGroup();

        try {
            // 创建服务器启动引导类
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    // 指定NIO传输Channel类型
                    .channel(NioServerSocketChannel.class)
                    // 设置服务器端连接队列大小
                    .option(ChannelOption.SO_BACKLOG, 128) // backlog
                    // 设置客户端连接保持活跃状态
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    // 设置子Channel的处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            // 配置Channel处理器管道
                            if (sslEnabled) {
                                // SSL处理器，用于WSS协议
                                ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));
                            }
                            
                            ch.pipeline()
                                    // HTTP编解码器，用于处理HTTP请求和响应
                                    .addLast(new HttpServerCodec())
                                    // 支持异步发送大的码流，但不占用过多的内存
                                    .addLast(new ChunkedWriteHandler())
                                    // 将HTTP消息的多个部分合成一条完整的HTTP消息
                                    .addLast(new HttpObjectAggregator(65536))
                                    // WebSocket协议处理器，处理握手、心跳等
                                    .addLast(new WebSocketServerProtocolHandler(path, null, true))
                                    // 自定义业务处理器
                                    .addLast(webSocketFrameHandler);
                        }
                    });

            // 绑定端口并启动服务器，绑定到0.0.0.0以接受外部连接
            channelFuture = bootstrap.bind("0.0.0.0", port).sync();
            String protocol = sslEnabled ? "WSS" : "WS";
            log.info("Netty {}服务器启动成功，端口: {}, 路径: {}, 绑定地址: 0.0.0.0", protocol, port, path);
        } catch (Exception e) {
            String protocol = sslEnabled ? "WSS" : "WS";
            log.error("Netty {}服务器启动失败", protocol, e);
            throw e;
        }
    }

    /**
     * 初始化SSL上下文
     */
    private void initSslContext() {
        try {
            ClassPathResource resource = new ClassPathResource(keystorePath);
            InputStream keystoreInputStream = resource.getInputStream();
            
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(keystoreInputStream, keystorePassword.toCharArray());
            
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
            
            sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
            log.info("SSL上下文初始化成功，使用证书: {}", keystorePath);
        } catch (Exception e) {
            log.error("SSL上下文初始化失败", e);
            throw new RuntimeException("SSL上下文初始化失败", e);
        }
    }

    /**
     * 关闭Netty WebSocket服务器
     * 在Spring容器销毁前自动调用
     */
    @PreDestroy
    public void stop() {
        // 关闭服务器Channel
        if (channelFuture != null) {
            channelFuture.channel().close();
        }
        // 优雅关闭主线程组
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        // 优雅关闭工作线程组
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
        String protocol = sslEnabled ? "WSS" : "WS";
        log.info("Netty {}服务器已关闭", protocol);
    }
}
